package naga.x.net.impl.websocket;

import static naga.x.net.impl.websocket.WebSocketConst.OP_BINARY;
import static naga.x.net.impl.websocket.WebSocketConst.PLAYLOAD_LEN_2_BYTES;
import static naga.x.net.impl.websocket.WebSocketConst.PLAYLOAD_LEN_8_BYTES;
import static naga.x.net.impl.websocket.WebSocketConst.PLAYLOAD_LEN_MASK;

import java.nio.ByteBuffer;

import naga.x.net.codec.PacketEncoder;
import naga.x.util.IoBufferWrapper;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WebSocketEncoder extends ProtocolEncoderAdapter implements PacketEncoder {

	protected static final Logger LOG = LoggerFactory.getLogger(WebSocketEncoder.class);
	
	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
		Object buf = encode(session, message);
		if (buf != null) {
			out.write(buf);
		}
	}
	
	private IoBuffer wrap(Object message) {
		if (message instanceof byte[]) {
			return IoBuffer.wrap((byte[]) message);
		}
		if (message instanceof ByteBuffer) {
			return IoBuffer.wrap((ByteBuffer) message);
		}
		if (message instanceof IoBufferWrapper) {
			return ((IoBufferWrapper) message).buf();
		}
//		if (message instanceof IoBuffer) {
//			return (IoBuffer) message;
//		}
		return null;
	}

	@Override
	public Object encode(IoSession session, Object packet) {
		IoBuffer msg = wrap(packet);
		if (msg == null) {
			LOG.error("[WEB SOCKET ENCODE]ERROR[MESSAGE WRAP NULL]" + session.getId());
			return null;
		}
		IoBuffer buf = IoBuffer.allocate(msg.limit() + 10).setAutoExpand(true);
		buf.put((byte) (0x80 | OP_BINARY)); // FIN RSV1 RSV2 RSV3 opcode(4bit)
		int playloadLen = msg.limit();
		if (playloadLen < PLAYLOAD_LEN_2_BYTES) {
			buf.put((byte) (playloadLen & PLAYLOAD_LEN_MASK));
		} else if (playloadLen < 65536) {
			buf.put((byte) (PLAYLOAD_LEN_2_BYTES & PLAYLOAD_LEN_MASK));
			buf.putShort((short) playloadLen);
		} else {
			buf.put((byte) (PLAYLOAD_LEN_8_BYTES & PLAYLOAD_LEN_MASK));
			buf.putLong(playloadLen);
		}
		// server->client don't need mask
		buf.put(msg); // put playload data
		buf.flip();
		return buf;
	}

}
